import { SubjectComponent } from "../../core/subject";
import Bullet, { BulletContact } from "./bullet";
import { BulletSide, PhysicsMath } from "./physics_math";
import { WorldData, WorldDataReadWrite } from "./world_data";

const { ccclass, property } = cc._decorator;


@ccclass
export default class World extends SubjectComponent {
    @property //是否对容器子BULLET进行边界约束
    isCapsule = false;

    @property({
        editorOnly: true,
    })
    editBounding = false;
    @property({
        visible() {
            return this.editBounding;
        },
    })
    get minX() {
        return this.node.x - this.node.anchorX * this.node.width;
    }
    set minX(val) {
        this.node.x = val + this.node.anchorX * this.node.width;
    }
    @property({
        visible() {
            return this.editBounding;
        },
    })
    get maxX() {
        return this.node.x + (1 - this.node.anchorX) * this.node.width;
    }
    set maxX(val) {
        this.node.x = val - (1 - this.node.anchorX) * this.node.width;
    }
    @property({
        visible() {
            return this.editBounding;
        },
    })
    get minY() {
        return this.node.y - this.node.anchorY * this.node.height;
    }
    set minY(val) {
        this.node.y = val + this.node.anchorY * this.node.height;
    }
    @property({
        visible() {
            return this.editBounding;
        },
    })
    get maxY() {
        return this.node.y + (1 - this.node.anchorX) * this.node.height;
    }
    set maxY(val) {
        this.node.y = val - (1 - this.node.anchorX) * this.node.height;
    }

    contacts: BulletContact[] = [];
    @property
    gravityInherit = true;
    @property({
        visible() {
            return !this.gravityInherit;
        },
    })
    gravity = cc.v2(0, -7000);

    bullets: Bullet[] = [];

    protected onLoad(): void {
        let bullets = this.getComponentsInChildren(Bullet);
        bullets.forEach(bullet => bullet.enterWorldByParent());
    }

    findBulletByUid(bulletUid: string) {
        return this.bullets.find(bullet => bullet.bulletUid === bulletUid);
    }
    findBulletByTag(tag: string) {
        return this.bullets.find(bullet => bullet.tag === tag);
    }
    //容器
    removeBullet(child: Bullet) {
        cc.js.array.remove(this.bullets, child);
    }
    addBullet(child: Bullet) {
        this.bullets.push(child);
    }
    private __contact_ind = -1;
    private __is_hit = false;

    getContactIndex(a: Bullet, b: Bullet) {
        return this.contacts.findIndex(ele => (ele.a === a && ele.b === b) || (ele.a === b && ele.b === a));
    }
    addContact(a: Bullet, b: Bullet) {
        let contact = new BulletContact();
        contact.a = a;
        contact.b = b;
        contact.abSide = PhysicsMath.CheckCollision(a.physicsRect, b.physicsRect, -50);
        if (contact.abSide == BulletSide.None) {
            return null;
        }

        let AEla = a.elastic / 100;
        let BEla = b.elastic / 100;
        contact.speedMag = a.speed.mul(AEla).sub(b.speed.mul(BEla)).mag();

        contact.abDock = !b.isSensor && contact.speedMag <= a.stick;
        contact.baDock = !a.isSensor && contact.speedMag <= b.stick;

        this.contacts.push(contact);
        return contact;
    }
    disposeContactsByChild(child: Bullet) {
        for (let i = 0; i < this.contacts.length;) {
            let contact = this.contacts[i];
            if (contact.a === child || contact.b === child) {
                this.contacts.splice(i, 1)[0].exit();
                this.log(`contact:${this.contacts.length}`);
            }
            else {
                i++;
            }
        }
    }

    protected update(dt: number): void {
        this.bullets.forEach(bullet => bullet.physicsUpdate(dt));

        for (let i = 0; i < this.bullets.length; i++) {
            let a = this.bullets[i];
            if (!cc.isValid(a)) {
                continue;
            }
            if (!a.physicsEnabled) {
                continue;
            }
            for (let j = i + 1; j < this.bullets.length; j++) {
                let b = this.bullets[j];
                if (!cc.isValid(b)) {
                    continue;
                }
                if (!b.physicsEnabled) {
                    continue;
                }
                if (a === b) {
                    continue;
                }
                if (!a.isDynamic && !b.isDynamic) {
                    continue;
                }

                if ((a.physicsGroup !== 0 && b.physicsGroup !== 0) || a.physicsGroup !== b.physicsGroup) {
                    continue;
                }

                this.__is_hit = cc.Intersection.rectRect(
                    a.physicsRect,
                    b.physicsRect
                );
                this.__contact_ind = this.getContactIndex(a, b);

                if (this.__contact_ind === -1) {
                    if (this.__is_hit) {
                        let contact = this.addContact(a, b);
                        if (!contact) {
                            continue;
                        }
                        contact.enter();
                        this.log(`contact:${this.contacts.length}`);
                        // this.log(`enter(${bullet.node.name},${otherBullet.node.name})`);
                    }
                }
                else {
                    if (this.__is_hit) {
                        let contact = this.contacts[this.__contact_ind];
                        //更新碰撞方向
                        contact.abSide = PhysicsMath.CheckCollision(a.physicsRect, b.physicsRect, -50);
                        if (contact.abSide !== BulletSide.None) {
                            contact.stay(dt);
                            // this.log(`stay(${bullet.node.name},${otherBullet.node.name})`);
                        }
                        else {
                            let contact = this.contacts.splice(this.__contact_ind, 1)[0];
                            contact.exit(); //碰撞结束
                            this.__contact_ind = -1;
                            // this.log(`exit(${bullet.node.name},${otherBullet.node.name})`);
                        }
                    }
                    else {
                        let contact = this.contacts.splice(this.__contact_ind, 1)[0];
                        contact.exit(); //碰撞结束
                        this.log(`contact:${this.contacts.length}`);
                        this.__contact_ind = -1;
                        // this.log(`exit(${bullet.node.name},${otherBullet.node.name})`);
                    }
                }
            }
        }
    }
    prevLog = "";
    log(str: string) {
        if (this.prevLog === str) {
            return;
        }
        this.prevLog = str;
        console.log(str);
    }


    //编辑器相关
    data: WorldData = null;
    initWithData(data: WorldData) {
        this.data = data;
        return WorldDataReadWrite.ReadDataToWorld(this, this.data.data);
    }
    //设置属性
    onMapEditorSetAttr(key: string) { }
};